package com.zz.client.config;

import com.zz.common.entity.ClientConfig;
import com.zz.common.function.Dispatch;
import com.zz.common.utils.ObjectUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 客户端缓存
 * 存放客户端连接信息
 * @author zz
 */
@Slf4j
public class ChannelCache {

    /**
     * 客户端主连接（授权连接）
     */
    @Getter
    private static Channel masterServerChannel;

    /**
     * 可并发连接数量
     */
    private static final AtomicInteger connectNum = new AtomicInteger(0);

    /**
     * 存放从服务器连接
     */
    private static final Queue<Channel> salveServerQueue = new LinkedBlockingQueue<>();

    /**
     * 设置主连接
     * @param channel
     */
    public static void setMasterServerChannel(Channel channel) {
        if (ObjectUtil.isNull(channel)) {
            return;
        }
        masterServerChannel = channel;
    }

    public static void clearMasterChannel() {
        masterServerChannel = null;
    }

    /**
     * 添加服务器连接（服务器连接复用）
     * @param channel
     */
    public static void addSalveServerChannel(Channel channel) {
        if (ObjectUtil.isNull(channel)) {
            log.error("salve server channel is null");
            return;
        }
        salveServerQueue.offer(channel);
    }

    /**
     * 从缓存channel池中移除连接
     * @param channel
     */
    public static void removeSalveServerChannel(Channel channel) {
        if (ObjectUtil.isNull(channel)) {
            return;
        }
        salveServerQueue.remove(channel);
    }

    /**
     * 从缓存channel池中获取连接（检查连接是否有效，无效：关闭 ）
     * @return
     * @throws Exception
     */
    private static Channel getSalveServerChannel() throws Exception {
        Channel channel = salveServerQueue.poll();
        if (ObjectUtil.isNotNull(channel) && !channel.isActive()) {
            removeConnectNum();
            channel.close();
            return getSalveServerChannel();
        }
        return channel;
    }

    /**
     * 绑定连接
     * @param bootstrap 客户端启动引导
     * @param context 上下文
     * @param config 本地映射配置
     * @param dispatch 绑定连接回调
     */
    public static void bindChannel(Bootstrap bootstrap, ChannelHandlerContext context, ClientConfig config, Dispatch<Channel> dispatch) {
        try {
            if (ObjectUtil.isEmptys(bootstrap, context, config, dispatch)) {
                log.error("连接绑定失败！，参数异常！");
                return;
            }
            Channel salveServerChannel = getSalveServerChannel();
            if (ObjectUtil.isNotNull(salveServerChannel)) {
                dispatch.accept(salveServerChannel);
                return;
            }
            synchronized (connectNum) {
                if (isMaxConnect(config)) {
                    dispatch.accept(null);
                    return;
                }
                TunnelClientConfig serverConfig = TunnelClientConfig.getInstance();
                bootstrap.connect(serverConfig.getServerBind(), serverConfig.getServerPort()).addListener((ChannelFutureListener) future -> {
                    if (future.isSuccess()) {
                        addConnectNum(config);
                        dispatch.accept(future.channel());
                        return;
                    }
                    dispatch.accept(null);
                });
            }

        } catch (Exception e) {
            log.error("bind channel fail:{}", e.getMessage());
        }

    }

    /**
     * 是否达到最大连接数
     * @param config
     * @return
     */
    private static boolean isMaxConnect(ClientConfig config) {
        if (ObjectUtil.isNull(config) || ObjectUtil.isEmpty(config.getClientId())) {
            return false;
        }
        return connectNum.get() >= config.getConcurrency();
    }

    /**
     * 增加链接数
     * @param config
     * @throws Exception
     */
    public static void addConnectNum(ClientConfig config) throws Exception {
        if (ObjectUtil.isNull(config) || ObjectUtil.isEmpty(config.getClientId())) {
            return;
        }
        if (isMaxConnect(config)) {
            return;
        }
        connectNum.incrementAndGet();

    }

    /**
     * 删除链接数
     * @throws Exception
     */
    public static void removeConnectNum() throws Exception {
        if (connectNum.get() <= 0) {
            log.error("连接数量异常！");
        }
        connectNum.decrementAndGet();
    }

}
